/*
  1、自定义颜色变量在该页面被导入，目前只导入5种变量，根据需求可以导入更多其他变量，格式自行参考现有模板。

  2、（1）elementTheme 变量为 element 自带的默认颜色，我们不需要引入其他任何覆盖 element 默认颜色的样式，
          默认颜色16进制可在 element 官网“自定义主题”一栏查看，我们需要在 APP.vue 中调用 THEME_ELEMENT() 
          将其替换为我们自己定义的颜色，示例（App.vue）：
          created() {
            this.$store.commit("theme/THEME_ELEMENT");
          }
     （2）因为element的css直接全局引入了，所以直接全局修改style来替换旧颜色即可，但是我们自己的样式，存在
          局部css，即只有当加载该页面时才会插入该页面style标签，所以需要监听 route 路由，当路由改变时，执
          行一次将我们自定义的默认颜色替换为当前主题的颜色的操作。注意：所有颜色变量使用16进制

  3、换肤组件路径：@/components/Theme/index.vue

  4、使用方法：
    （1）在 myColor 里面定义自己想要换肤的主题类型和颜色，格式参考模板，elementTheme 参数非必须，一般只需要覆盖
        element的主题色、成功色、失败色、警告色、信息色已经足够。css变量定义地址：@/assets/scss/variable.scss
    （2）如果你的自定义色调和element色调不一致（一般都不会一致），建议在 app.vue 的 created 或之后的生命周期
        中,调用 THEME_ELEMENT() 方法，示例（App.vue）：
        created() {
          this.$store.commit("theme/THEME_ELEMENT");
        }
    （3）因为我们的 css 存在局部样式，更换主题时，某些页面的 style 标签可能还没有被渲染，所以在每次路由切换时，
        都要执行一次将自定义的默认色替换为当前主题色的操作，避免换肤的漏网之鱼，示例（App.vue）：
        watch: {
          $route() {
            this.$store.commit("theme/THEME_DEFAULT");
          }
        }
    （4）需要更换色调颜色时，调用 THEME_CHANGE() 函数，optionArr -> [option] option对象传入2个参数，
        val：替换后的颜色，如“#aaa”，type：替换的颜色类型，如“primary”

  5、按道理，你只需要修改 myColor 和调用 mutations 里面的方法即可，其他请勿改动
*/
import variables from "@/assets/scss/variable.scss";
const defaultVariables = JSON.parse(JSON.stringify(variables));
const myColor = [
  // 主题色
  { type: "primary", elementTheme: "#409EFF", theme: variables.primary },
  // 成功色
  { type: "success", elementTheme: "#67c23a", theme: variables.success },
  // 失败色
  { type: "danger", elementTheme: "#f56c6c", theme: variables.danger },
  // 警告色
  { type: "warning", elementTheme: "#e6a23c", theme: variables.warning },
  // 信息色
  { type: "info", elementTheme: "#909399", theme: variables.info }
];

const state = {
  buffer: false, // 是否缓存主题色
  themeList: getThemeList()
};

const mutations = {
  // 将自定义默认色替换成当前色
  THEME_DEFAULT(state) {
    const optionArr = [];
    state.themeList.forEach(item => {
      if (item.theme !== item.defaultTheme) {
        optionArr.push({
          val: item.theme,
          type: item.type
        });
      }
    });
    changeColor(state.themeList, optionArr, "default");
  },
  // 将element与自定义颜色同步
  THEME_ELEMENT(state) {
    const optionArr = [];
    state.themeList.forEach(item => {
      if (item.elementTheme && item.elementTheme !== item.theme) {
        optionArr.push({
          val: item.theme,
          type: item.type
        });
      }
    });
    changeColor(state.themeList, optionArr, "element");
  },
  // 恢复默认主题
  THEME_RESET(state) {
    const optionArr = [];
    state.themeList.forEach(item => {
      optionArr.push({
        val: item.defaultTheme,
        type: item.type
      });
    });
    changeColor(state.themeList, optionArr, "reset");
    bufferTheme(state);
  },
  // 修改主题（optionArr -> [option]：option对象传入2个参数：val（替换后的颜色），type（替换的颜色类型））
  THEME_CHANGE(state, optionArr) {
    changeColor(state.themeList, optionArr, "custor");
    bufferTheme(state);
  }
};

export default {
  namespaced: true,
  state,
  mutations
};

// 缓存主题
function bufferTheme(state) {
  if (!state.buffer) return;
  const THEME_BUFFER = [];
  state.themeList.forEach(item => {
    THEME_BUFFER.push({
      val: item.theme,
      type: item.type
    });
  });
  localStorage.setItem("THEME_BUFFER", JSON.stringify(THEME_BUFFER));
}

// 处理state数据
function getThemeList() {
  const themeList = [];
  myColor.forEach(item => {
    themeList.push({
      type: item.type,
      elementTheme: item.elementTheme, // element需要替换的默认颜色（旧）,非必须
      oldTheme: item.theme, // 需要替换的自定义旧颜色（旧），必须
      theme: item.theme, // 目前的主题色（新），必须
      defaultTheme: defaultVariables[item.type] // 默认的自定义颜色（固定），必须
    });
  });
  return themeList;
}

// 通过type获取当前主题列表中的主题
function getValByType(themeList, type) {
  let themeItem = {};
  themeList.forEach(item => {
    if (item.type === type) {
      themeItem = item;
    }
  });
  return themeItem;
}

// 换肤
function changeColor(themeList, optionArr, from) {
  const newThemeCluster = []; // 替换颜色集合（新）
  const oldThemeCluster = []; // 被替换的颜色集合（旧）
  optionArr.forEach(option => {
    const { val, type } = option;
    if (!val || !type) return;
    let oldVal = "";
    const themeItem = getValByType(themeList, type);
    switch (from) {
      case "default":
        oldVal = themeItem.defaultTheme;
        break;
      case "element":
        oldVal = themeItem.elementTheme;
        themeItem.elementTheme = val;
        break;
      case "custor":
        oldVal = themeItem.oldTheme;
        themeItem.oldTheme = val;
        themeItem.theme = val;
        break;
      case "reset":
        oldVal = themeItem.theme;
        themeItem.oldTheme = val;
        themeItem.theme = val;
        break;
    }
    if (oldVal === val) return;
    newThemeCluster.push(...getThemeCluster(val.replace("#", "")));
    oldThemeCluster.push(...getThemeCluster(oldVal.replace("#", "")));
  });
  if (!newThemeCluster.length) return;
  const styles = [].slice
    .call(document.querySelectorAll("style"))
    .filter(style => {
      const html = style.innerHTML;
      let flag = false;
      for (let i = 0; i < oldThemeCluster.length; i++) {
        if (new RegExp(oldThemeCluster[i], "i").test(html)) {
          flag = true;
          break;
        }
      }
      return flag;
    });
  styles.forEach(style => {
    const { innerHTML } = style;
    if (typeof innerHTML !== "string") return;
    style.innerHTML = updateStyle(innerHTML, oldThemeCluster, newThemeCluster);
  });
}

// 通过16进制获取rgb色并获取其他副色
function getThemeCluster(theme) {
  const clusters = [theme];
  const tintColor = (color, tint) => {
    if (color.length === 3) {
      const themeArr = theme.split("");
      const color1 = themeArr[0];
      const color2 = themeArr[1];
      const color3 = themeArr[2];
      color = color1 + color1 + color2 + color2 + color3 + color3;
    }
    let red = parseInt(color.slice(0, 2), 16);
    let green = parseInt(color.slice(2, 4), 16);
    let blue = parseInt(color.slice(4, 6), 16);
    if (tint === 0) {
      // rgb颜色
      clusters.push([red, green, blue].join(","));
      clusters.push([red, green, blue].join(", ")); // 有时rgb之间会有空格
    } else {
      // element hover颜色
      red += Math.round(tint * (255 - red));
      green += Math.round(tint * (255 - green));
      blue += Math.round(tint * (255 - blue));
      red = red.toString(16);
      green = green.toString(16);
      blue = blue.toString(16);
      clusters.push(`#${red}${green}${blue}`);
    }
  };
  // element focus颜色
  const shadeColor = (color, shade) => {
    let red = parseInt(color.slice(0, 2), 16);
    let green = parseInt(color.slice(2, 4), 16);
    let blue = parseInt(color.slice(4, 6), 16);
    red = Math.round((1 - shade) * red);
    green = Math.round((1 - shade) * green);
    blue = Math.round((1 - shade) * blue);
    red = red.toString(16);
    green = green.toString(16);
    blue = blue.toString(16);
    clusters.push(`#${red}${green}${blue}`);
  };
  for (let i = 0; i <= 9; i++) {
    tintColor(theme, Number((i / 10).toFixed(2)));
  }
  shadeColor(theme, 0.1);
  return clusters;
}

// 替换style
function updateStyle(style, oldCluster, newCluster) {
  let newStyle = style;
  oldCluster.forEach((color, index) => {
    newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]);
  });
  return newStyle;
}
